import logging
import os
import datetime
import traceback
import logging
import inspect
from configs.config import LOG_CONFIG

class Logger:
    def __init__(self, log_dir="logs"):
        # 创建日志目录
        self.log_dir = log_dir
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)

        # 配置日志格式 - 增强格式，添加线程信息和文件名行号
        self.log_format = "%(asctime)s - %(name)s - %(levelname)s - [%(threadName)s] - %(source_file)s:%(source_line)s - %(message)s"
        self.date_format = "%Y-%m-%d %H:%M:%S"

        # 创建logger
        self.logger = logging.getLogger("exam_system")
        
        # 根据配置设置日志级别
        log_level_map = {
            'DEBUG': logging.DEBUG,
            'INFO': logging.INFO,
            'WARNING': logging.WARNING,
            'ERROR': logging.ERROR,
            'CRITICAL': logging.CRITICAL
        }
        
        # 设置全局日志级别
        self.logger.setLevel(log_level_map.get(LOG_CONFIG['level'], logging.INFO))

        # 清除已存在的处理器
        if self.logger.handlers:
            self.logger.handlers.clear()

        # 创建文件处理器
        today = datetime.datetime.now().strftime("%Y-%m-%d")
        log_file = os.path.join(log_dir, f"{today}.log")
        file_handler = logging.FileHandler(log_file, encoding="utf-8")
        file_handler.setLevel(log_level_map.get(LOG_CONFIG['file_level'], logging.INFO))
        file_formatter = logging.Formatter(self.log_format, self.date_format)
        file_handler.setFormatter(file_formatter)
        self.logger.addHandler(file_handler)
        
        # 创建控制台处理器（用于开发环境下同时在控制台显示日志）
        console_handler = logging.StreamHandler()
        # 控制台日志级别设置为与全局日志级别相同
        console_handler.setLevel(log_level_map.get(LOG_CONFIG['level'], logging.INFO))
        # 控制台日志可以使用简化格式
        console_formatter = logging.Formatter(self.log_format, self.date_format)
        console_handler.setFormatter(console_formatter)
        self.logger.addHandler(console_handler)

        # 根据配置清理旧日志
        if LOG_CONFIG.get('clean_old_logs', True):
            self.clean_old_logs()

    def get_logger(self):
        return self.logger

    def info(self, message, source_file="unknown", source_line=0):
        extra = {'source_file': source_file, 'source_line': source_line}
        self.logger.info(message, extra=extra)

    def warning(self, message, source_file="unknown", source_line=0):
        extra = {'source_file': source_file, 'source_line': source_line}
        self.logger.warning(message, extra=extra)

    def error(self, message, source_file="unknown", source_line=0):
        extra = {'source_file': source_file, 'source_line': source_line}
        self.logger.error(message, extra=extra)

    def exception(self, message, source_file="unknown", source_line=0, exc_info=True):
        # 详细记录异常信息
        extra = {'source_file': source_file, 'source_line': source_line}
        self.logger.error(f"{message}\n{traceback.format_exc()}", exc_info=exc_info, extra=extra)
        
    def debug(self, message, source_file="unknown", source_line=0):
        extra = {'source_file': source_file, 'source_line': source_line}
        self.logger.debug(message, extra=extra)
        
    def critical(self, message, source_file="unknown", source_line=0):
        extra = {'source_file': source_file, 'source_line': source_line}
        self.logger.critical(message, extra=extra)

    def clean_old_logs(self):
        """清理过期日志文件"""
        try:
            # 获取保留天数配置
            retention_days = LOG_CONFIG.get('log_retention_days', 30)
            
            # 计算过期日期
            expire_date = datetime.datetime.now() - datetime.timedelta(days=retention_days)
            expire_date_str = expire_date.strftime("%Y-%m-%d")

            # 获取日志目录下的所有文件
            for filename in os.listdir(self.log_dir):
                # 检查文件名是否符合日期格式
                if len(filename) == 14 and filename.endswith(".log"):
                    file_date_str = filename[:10]
                    # 如果文件日期早于过期日期，则删除
                    if file_date_str < expire_date_str:
                        file_path = os.path.join(self.log_dir, filename)
                        os.remove(file_path)
                        self.logger.info(f"已删除过期日志文件: {filename}", extra={'source_file': 'logger.py', 'source_line': 88})
        except Exception as e:
            self.logger.error(f"清理旧日志时出错: {str(e)}", extra={'source_file': 'logger.py', 'source_line': 96})

# 创建全局logger实例
logger = Logger().get_logger()

# 获取调用者的文件名和行号

def get_caller_info():
    # 获取调用栈信息
    frame = inspect.currentframe().f_back.f_back  # 跳过当前函数和log_xxx函数
    filename = inspect.getfile(frame)
    lineno = frame.f_lineno
    # 返回文件名（不含路径）和行号
    return os.path.basename(filename), lineno

# 自定义日志函数，正确记录调用位置

def log_info(message):
    source_file, source_line = get_caller_info()
    logger.info(message, extra={'source_file': source_file, 'source_line': source_line})

def log_debug(message):
    source_file, source_line = get_caller_info()
    logger.debug(message, extra={'source_file': source_file, 'source_line': source_line})

def log_warning(message):
    source_file, source_line = get_caller_info()
    logger.warning(message, extra={'source_file': source_file, 'source_line': source_line})

def log_error(message):
    source_file, source_line = get_caller_info()
    logger.error(message, extra={'source_file': source_file, 'source_line': source_line})

def log_critical(message):
    source_file, source_line = get_caller_info()
    logger.critical(message, extra={'source_file': source_file, 'source_line': source_line})

def log_exception(message):
    source_file, source_line = get_caller_info()
    # 详细记录异常信息，包含完整的堆栈跟踪
    logger.error(f"{message}\n{traceback.format_exc()}", exc_info=True, extra={'source_file': source_file, 'source_line': source_line})

# 自动捕获所有未处理的异常并记录到日志
def setup_exception_handler():
    import sys
    original_excepthook = sys.excepthook
    
    def custom_excepthook(exc_type, exc_value, exc_traceback):
        # 记录未处理的异常
        if issubclass(exc_type, KeyboardInterrupt):
            # 不记录用户中断
            original_excepthook(exc_type, exc_value, exc_traceback)
            return
        
        # 生成详细的异常信息，包含异常类型
        error_message = "未处理的异常: {}: {}".format(exc_type.__name__, str(exc_value))
        
        # 从堆栈跟踪中获取异常发生的文件名和行号
        source_file = "unknown"
        source_line = 0
        if exc_traceback:
            # 获取堆栈信息
            tb_frame = exc_traceback.tb_frame
            source_file = os.path.basename(tb_frame.f_code.co_filename)
            source_line = exc_traceback.tb_lineno
        
        # 使用logging模块的error方法直接记录完整的堆栈信息，包含正确的文件名和行号
        logger.error(
            error_message,
            exc_info=(exc_type, exc_value, exc_traceback),
            extra={'source_file': source_file, 'source_line': source_line}
        )
        
        # 调用原始的异常处理函数
        original_excepthook(exc_type, exc_value, exc_traceback)
    
    # 设置自定义异常处理器
    sys.excepthook = custom_excepthook

# 设置自动异常捕获
setup_exception_handler()

